from django.shortcuts import render, reverse, HttpResponse
from django.http import JsonResponse
from django.forms import model_to_dict
from django.views.decorators.csrf import csrf_exempt
from django.utils.encoding import escape_uri_path
from web.forms.file import FolderModelForm, FileSaveModelForm
from web import models
from utils.tencent.cos import delete_file, delete_file_list, credential
import json, time, requests


# http://127.0.0.1:8002/manage/1/file/
# http://127.0.0.1:8002/manage/1/file/?folder=1
def file(request, project_id):
    """
    添加文件夹
    查看文件列表
    :param request:
    :param project_id:
    :return:
    """

    parent_object = None
    folder_id = request.GET.get('folder', "")
    if folder_id.isdecimal():
        parent_object = models.FileRepository.objects.filter(id=int(folder_id), file_type=2,
                                                             project=request.tracer.project).first()

    if request.method == "GET":

        breadcrumb_list = []
        parent = parent_object
        while parent:
            # breadcrumb_list.insert(0, {'id': parent.id, 'name': parent.name})
            breadcrumb_list.insert(0, model_to_dict(parent, ['id', 'name']))
            parent = parent.parent

        # 查看文件列表
        queryset = models.FileRepository.objects.filter(project=request.tracer.project)
        # 依据parent_object 的存在与否
        if parent_object:
            # 如果 parent_object 存在，即表明进入了某个目录
            file_object_list = queryset.filter(parent=parent_object).order_by('-file_type')

        else:
            # parent_objec 不存在即当前处于根目录
            file_object_list = queryset.filter(parent__isnull=True).order_by('-file_type')

        form = FolderModelForm(request, parent_object)
        return render(request, 'file.html', {
            'form': form,
            'file_object_list': file_object_list,
            'breadcrumb_list': breadcrumb_list,
            'folder_object': parent_object
        })

    # 从用户提交的数据中获取fid（判别添加还是编辑的依据）
    fid = request.POST.get('fid', '')

    # 设置要编辑的对象默认为空
    edit_object = None

    # 如果fid存在
    if fid.isdecimal():
        # 编辑文件夹
        edit_object = models.FileRepository.objects.filter(id=int(fid), file_type=2,
                                                           project=request.tracer.project).first()
    # 要编辑的对象存在的情况下
    if edit_object:
        form = FolderModelForm(request, parent_object, data=request.POST, instance=edit_object)

    else:
        # 添加文件夹
        form = FolderModelForm(request, parent_object, data=request.POST)

    if form.is_valid():
        form.instance.project = request.tracer.project
        form.instance.file_type = 2
        form.instance.update_user = request.tracer.user
        form.instance.parent = parent_object
        form.save()
        return JsonResponse({'status': True})

    return JsonResponse({'status': False, 'error': form.errors})


# http://127.0.0.1:8002/manage/1/file/delete/?fid=1
def file_delete(request, project_id):
    """
    文件/文件夹删除
    :param request:
    :param project_id:
    :return:
    """
    fid = request.GET.get('fid')

    # 在数据库中删除文件夹或者文件
    delete_object = models.FileRepository.objects.filter(id=fid, project=request.tracer.project).first()

    if delete_object.file_type == 1:
        # 删除文件（数据库文件删除、cos文件删除、项目已使用空间容量还回去）

        # 一、恢复此文件所占项目空间
        request.tracer.project.use_space -= delete_object.file_size / 1024
        request.tracer.project.save()

        # 二、在腾讯云对象存储中删除此文件
        delete_file(
            request.tracer.project.bucket,
            request.tracer.project.region,
            delete_object.key
        )

        # 三、在数据库中删除此文件
        delete_object.delete()

        return JsonResponse({'status': True})

    # 删除文件夹（找到文件夹下所有的文件->数据库文件删除、cos文件删除、项目已使用空间容量还回去）
    # delete_object
    # 找他下面的 文件和文件夹
    # models.FileRepository.objects.filter(parent=delete_object) # 文件 删除；文件夹 继续向里差

    total_size = 0
    key_list = []

    folder_list = [delete_object, ]
    for folder in folder_list:
        child_list = models.FileRepository.objects.filter(project=request.tracer.project, parent=folder).order_by(
            '-file_type')
        for child in child_list:
            if child.file_type == 2:
                folder_list.append(child)
            else:
                # 文件大小汇总
                total_size += child.file_size

                # 删除文件
                key_list.append({"Key": child.key})

    # 一、在腾讯云对象存储中批量删除文件
    if key_list:
        delete_file_list(request.tracer.project.bucket, request.tracer.project.region, key_list)

    # 二、恢复文件列表中所有文件所占项目空间
    if total_size:
        request.tracer.project.use_space -= total_size
        request.tracer.project.save()

    # 三、删除数据库中的文件
    delete_object.delete()
    return JsonResponse({'status': True})


@csrf_exempt
def cos_credential(request, project_id):
    """
    一、对用户上传的单文件大小与当前用户所拥有的权限做比对；
    二、将用户本次上传的文件的总大小与当前项目所占空间还剩余的空间大小做比对；
    三、当一二两条限制都比对通过的情况下，调用utils.tencent.cos.credential函数
        获取往腾讯云 cos 中上传文件的临时凭证
    :param request:
    :return:
    """
    # 一、对用户上传的单文件大小进行限制

    per_file_limit = request.tracer.price_policy.per_file_size * 1024
    # 数据库中 price_policy 表所存 per_file_size 字段的单位是 M 转化为 : (M * 1024)KB

    total_size = 0
    # 用户所传文件的总大小 total_size

    file_list = json.loads(request.body.decode('utf-8'))
    # file_list 是用户选择上传的文件的名称跟大小的列表（用户可选择多个文件同时上传）
    # [{'name':'xxxxxx.png', 'size':70812}, {'name':'xxxxxxxxx.png', 'size':71922}]
    for item in file_list:
        if item['size'] / 1024 > per_file_limit:
            return JsonResponse({'status': False,
                                 'error': f"单文件超出限制 (最大{request.tracer.price_policy.per_file_size}M)，文件：{item['name']}，若想继续上传，请充值！"})
        total_size += item['size'] / 1024

    # 二、对用户所上传的文件的总大小进行限制

    # 当前用户所拥有的权限中单个项目所占最大空间 KB
    project_max_space = request.tracer.price_policy.project_space * 1024 * 1024

    # 当前所创建项目已使用的空间 KB
    current_project_use_space = request.tracer.project.use_space

    # 当前项目剩余的空间
    remainder_project_space = project_max_space - current_project_use_space

    if total_size > remainder_project_space:
        # 本次上传文件的总大小大于项目所剩空间
        return JsonResponse({'status': False,
                             'error':
                                 f"当前项目剩余空间为：{remainder_project_space}, 本次上传文件大小为：{total_size}，已超过权限范围，若想继续上传，请充值！"})

    bucket = request.tracer.project.bucket
    region = request.tracer.project.region
    data_dict = credential(bucket, region)
    return JsonResponse({'status': True, 'data': data_dict})


@csrf_exempt
def file_save(request, project_id):
    """
    将用户通过临时凭证上传到 cos 中的文件的相关信息保存到数据库中
    相关信息为：
            name: fileName,
            file_size: fileSize,
            key: fileKey,
            parent: CURRENT_FOLDER_ID,
            file_path: data.Location,
            etag: data.ETag,
    :param request:
    :param project_id:
    :return:
    """
    # 根据key再去cos获取文件Etag和"db7c0d83e50474f934fd4ddf059406e5"

    print(request.POST)
    # 把获取到的数据写入数据库即可
    form = FileSaveModelForm(request, request.POST)
    if form.is_valid():
        # 通过ModelForm.save存储到数据库中的数据返回的isntance对象，无法通过get_xx_display获取choice的中文
        # form.instance.file_type = 1
        # form.update_user = request.tracer.user
        # instance = form.save() # 添加成功之后，获取到新添加的那个对象（instance.id,instance.name,instance.file_type,instace.get_file_type_display()

        # 校验通过：数据写入到数据库
        data_dict = form.cleaned_data
        data_dict.pop('etag')
        data_dict.update({'project': request.tracer.project, 'file_type': 1, 'update_user': request.tracer.user})
        instance = models.FileRepository.objects.create(**data_dict)

        # 项目的已使用空间：更新 (data_dict['file_size'])
        request.tracer.project.use_space += data_dict['file_size'] / 1024
        request.tracer.project.save()

        result = {
            'id': instance.id,
            'name': instance.name,
            'file_size': instance.file_size,
            'username': instance.update_user.username,
            'datetime': instance.update_datetime.strftime('%Y{y}%m{m}%d{d} %H{h}%M{f}%S{s}').format(y='年', m='月', d='日',
                                                                                                    h='时', f='分',
                                                                                                    s='秒'),
            'download_url': reverse('file_download', kwargs={'project_id': project_id, 'file_id': instance.id})
            # 'file_type': instance.get_file_type_display()
        }
        return JsonResponse({'status': True, 'data': result})

    return JsonResponse({'status': False, 'data': "文件错误"})


def file_download(request, project_id, file_id):
    """
    文件下载
    一、从数据库中获取当前文件在 cos 中所存储的 file_path 路径
    二、利用第三方模块requests根据文件路径获取当前文件的内容
    三、为文件内容设置响应头
    :param request:
    :param project_id:
    :param file_id:
    :return:
    """
    file_object = models.FileRepository.objects.filter(project_id=project_id, id=file_id).first()
    res = requests.get(file_object.file_path)
    data = res.content

    response = HttpResponse(data)
    # 设置响应头
    response['Content-Disposition'] = f"attachment; filename={(escape_uri_path(file_object.name))}"
    return response

